package cloud.xlh.infrastructure.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Stream;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2022/12/6 21:37
 * @ Description:
 */
@Slf4j
public class FileUtil {

    /**
     * @param pathClassCode 路径类型
     * @param filePath      文件路径
     * @return java.lang.String
     * @author XuLeHuang
     * @create 2023/2/13
     * @name:
     * @Description: 从任意文件读取内容并转化为字符串
     **/
    public static String readCommonFileAndToStr(Integer pathClassCode, String filePath) {
        if (PathTypeCode.CLASS_PATH.code.equals(pathClassCode)) {
            URL url = FileUtil.class.getClassLoader().getResource(filePath);
            if (url != null) {
                URI uri = null;
                Path path = null;
                String fileStr = null;
                try {
                    uri = url.toURI();
                    path = Paths.get(uri);
                    byte[] bytes = Files.readAllBytes(path);
                    fileStr = new String(bytes);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("路径不存在");
                }
                return fileStr;
            } else {
                log.error("路径->{}不存在，请重新选择！！！", filePath);
                throw new RuntimeException("路径不存在");
            }
        } else if (PathTypeCode.ABSOLUTE_PATH.code.equals(pathClassCode)) {
            FileInputStream fileInputStream = null;
            BufferedInputStream bufferedInputStream = null;
            StringBuilder content = new StringBuilder("");
            try {
                fileInputStream = new FileInputStream(filePath);
                bufferedInputStream = new BufferedInputStream(fileInputStream);
                byte[] bytes = new byte[1024 * 8];
                int len = 0;
                while((len = bufferedInputStream.read(bytes)) != -1)
                {
                    content.append(new String(bytes, 0, len));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (bufferedInputStream != null) {
                        bufferedInputStream.close();
                    }
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }

                } catch (Exception e) {

                }
            }
            return content.toString();
        } else {
            log.error("路径类型枚举->{}不存在，请重新选择！！！", pathClassCode);
            throw new RuntimeException("路径类型枚举不存在");
        }
    }

    /**
     * @param pathClassCode 路径类型
     * @param filePath      文件路径
     * @return com.alibaba.fastjson.JSONObject
     * @author XuLeHuang
     * @create 2023/2/13
     * @name:
     * @Description: 将文件内容转化成JSONObject
     **/
    public static JSONObject FileStrToJSONObject(Integer pathClassCode, String filePath) {
        try {
            String fileStr = readCommonFileAndToStr(pathClassCode, filePath);
            JSONObject fileToJsonObject = JSON.parseObject(fileStr);
            return fileToJsonObject;
        } catch (Exception e) {
            throw new RuntimeException("文件读取失败");
        }

    }

    /**
     * @param pathClassCode 路径类型
     * @param map           待转化的map数据
     * @param filePath      json文件路径
     * @return void
     * @author XuLeHuang
     * @create 2023/2/13
     * @name:
     * @Description: 将map内容写入json文件
     **/
    public static void writeMapJavaObjectToFile(Integer pathClassCode, Map map, String filePath) throws URISyntaxException, IOException {
        if (PathTypeCode.CLASS_PATH.code.equals(pathClassCode)) {
            URL url = FileUtil.class.getClassLoader().getResource(filePath);
            if (url != null) {
                URI uri = url.toURI();
                Path path = Paths.get(uri);
                String fileStr = JSON.toJSONString(map);
                Files.write(path, fileStr.getBytes(StandardCharsets.UTF_8));
            } else {
                log.error("路径->{}不存在，请重新选择！！！", filePath);
                throw new RuntimeException("路径不存在");
            }
        } else if (PathTypeCode.ABSOLUTE_PATH.code.equals(pathClassCode)) {

        } else {
            log.error("路径类型枚举->{}不存在，请重新选择！！！", pathClassCode);
            throw new RuntimeException("路径类型枚举不存在");
        }
    }

    /**
     * @param pathTypeCode 路径类型
     * @param filePath     文件路径
     * @return java.util.List<java.lang.String>
     * @author XuLeHuang
     * @create 2023/2/13
     * @name:
     * @Description: 文件遍历
     **/
    public static List<String> walkFileAndReturnFileNameList(Integer pathTypeCode, String filePath) throws IOException {
        List<String> fileNameList = new ArrayList<>();

        /**
         * 排除文件夹
         * */
        try (Stream<Path> pathStream = Files.walk(Paths.get("D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\common\\infrastructure\\src"))) {
            pathStream.filter(Files::isRegularFile).forEach(file -> {
                System.out.println(file.toString());
                fileNameList.add(file.toString());
                System.out.println(file.getFileName().toString());
            });
        }

        Files.walkFileTree(Paths.get("D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\common\\infrastructure\\src"), new FileVisitor<Path>() {

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

                if (file.toString().endsWith(".java")) {
                    List<String> strings = Files.readAllLines(file,
                            StandardCharsets.UTF_8);
                    System.out.println(strings);
                    if (strings.toString().contains("FileVisitResult")) {
                        log.info("文件{}", file);
                    }
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }
        });
        return fileNameList;
    }

    /**
     * @param pathTypeCode 路径类型
     * @param xmlPath      文件路劲
     * @return java.lang.Object
     * @author XuLeHuang
     * @create 2023/2/13
     * @name:
     * @Description: xml文件内容转化成java对象
     **/
    public static Object xmlToJavaObject(String pathTypeCode, String xmlPath) throws URISyntaxException, IOException {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            System.out.println(stackTraceElement.getMethodName());
            System.out.println(stackTraceElement.getClassName());
            System.out.println(stackTraceElement.getLineNumber());
        }
        String fileAndToStr = readCommonFileAndToStr(PathTypeCode.CLASS_PATH.code, xmlPath);
       /* String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
                "    <User>\n" +
                "        <Name>2</Name>\n" +
                "        <Sex>2</Sex>\n" +
                "        <Age>2</Age>\n" +
                "        <Course>\n" +
                "            <Name>chinese</Name>\n" +
                "            <Score>100</Score>\n" +
                "        </Course>\n" +
                "    </User>";*/
        StringReader reader = new StringReader(fileAndToStr);
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            User user = (User) jaxbUnmarshaller.unmarshal(reader);
            log.info("xml->javaObject:{}", user.toString());
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * @param pathTypeCode 路径类型
     * @param filePath 文件路径
     * @param fromXML 是否从XML文件加载
     * @return java.util.Properties
     * @author XuLeHuang
     * @create 2023/2/15
     * @name:
     * @Description: 读取KeyProperties文件
     **/
    public static Properties readKeyPropertiesFile(Integer pathTypeCode, String filePath , boolean fromXML) {
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        Properties properties = new Properties();
        if (PathTypeCode.ABSOLUTE_PATH.code == pathTypeCode) {
            try {
                fileInputStream = new FileInputStream(filePath);
                bufferedInputStream = new BufferedInputStream(fileInputStream);
                try {
                    if (!fromXML) {
                        properties.load(bufferedInputStream);
                    } else {
                        properties.loadFromXML(bufferedInputStream);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (bufferedInputStream != null) {
                        bufferedInputStream.close();
                    }
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                    throw new RuntimeException("输入流资源关闭失败");
                }
            }
        } else if (PathTypeCode.CLASS_PATH.code == pathTypeCode) {

        } else {

        }
        return properties;
    }

    /**
     *
     * @param properties properties
     * @param fileName 文件名
     * @param filePath 文件夹路径
     * @param pathTypeCode 路径类型
     * @return void
     * @author XuLeHuang
     * @create 2023/2/15
     * @name:
     * @Description:
     **/
    public static void propertiesStoreToXML(Properties properties , String fileName , String filePath , Integer pathTypeCode) {
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(filePath);
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            properties.storeToXML(bufferedOutputStream , fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param pathName 文件路径
     * @param pathTypeCode 路径类型
     * @return java.util.Map
     * @author XuLeHuang
     * @create 2023/2/15
     * @name:
     * @Description: 读取yml文件
     **/
    public static Map readApplicationYMLFile(String pathName, Integer pathTypeCode) {
        //可以加载多个yml文件
        YamlMapFactoryBean yamlMapFactoryBean = new YamlMapFactoryBean();
        if (PathTypeCode.ABSOLUTE_PATH.getCode() == pathTypeCode) {
            try {

                yamlMapFactoryBean.setResources(new InputStreamResource(new BufferedInputStream(new FileInputStream(pathName))));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        } else if (PathTypeCode.CLASS_PATH.getCode() == pathTypeCode) {
            yamlMapFactoryBean.setResources(new ClassPathResource(pathName));
        } else {

        }
        //通过getObject()方法获取Map对象
        Map<String, Object> map = yamlMapFactoryBean.getObject();
        System.out.println(map);
        /*map.keySet().forEach(item -> {
            //可以将map中的值强转为LinkedHashMap对象
            LinkedHashMap<String, Object> linkedHashMap = (LinkedHashMap<String, Object>) (map.get(item));
            System.out.println(linkedHashMap.get("tels"));
        });*/
        return map;
    }

    /**
     *
     * @param pathName 文件路径
     * @param pathTypeCode 路径类型
     * @return java.util.Properties
     * @author XuLeHuang
     * @create 2023/2/15
     * @name:
     * @Description: 读取yml文件并转化成Properties格式
     **/
    public static Properties readApplicationYMLFileAndTurnToProperties(String pathName, Integer pathTypeCode) {
        //可以加载多个yml文件
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean ();
        if (PathTypeCode.ABSOLUTE_PATH.getCode() == pathTypeCode) {
            try {
                yamlPropertiesFactoryBean.setResources(new InputStreamResource(new BufferedInputStream(new FileInputStream(pathName))));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        } else if (PathTypeCode.CLASS_PATH.getCode() == pathTypeCode) {
            yamlPropertiesFactoryBean.setResources(new ClassPathResource(pathName));
        } else {

        }
        //通过getObject()方法获取Map对象
        Properties properties = yamlPropertiesFactoryBean.getObject();
        System.out.println(properties);
        properties.keySet().forEach(key -> System.out.println(key + "=====" + properties.get(key)));
        return properties;
    }

    /**
     * @param aClass       实体类型
     * @param dataList     数据列表
     * @param filePath     文件导出地址
     * @param fileName     文件名
     * @param pathTypeCode 路径类型
     * @return void
     * @author XuLeHuang
     * @create 2023/2/13
     * @name: excel文件导出
     * @Description:
     **/
    public static void exportToExcel(Class aClass, List<Object> dataList, String filePath, String fileName, Integer pathTypeCode) {
        File folder = new File(filePath);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }
        if (StringUtil.isEmpty(fileName)) {
            fileName = filePath + aClass.getSimpleName() + System.currentTimeMillis() + ".xlsx";
        }

        // 这里 需要指定写用哪个class去写，然后写到第一个sheet，名字为用户表 然后文件流会自动关闭
        EasyExcel.write(fileName, aClass).sheet(aClass.getSimpleName() + "表").doWrite(dataList);
    }

    /**
     * @param multipartFile
     * @param aClass        实体类型
     * @return java.util.List<java.lang.Object>
     * @author XuLeHuang
     * @create 2023/2/13
     * @name: excel文件导入
     * @Description:
     **/
    public static List<Object> imPortToExcel(MultipartFile multipartFile, Class aClass) {
        if (multipartFile.isEmpty()) {
            return null;
        }
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        // 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行
        List<Object> dataListFromReturn = new ArrayList<>();
        try {
            EasyExcel.read(multipartFile.getInputStream(), aClass, new PageReadListener<Object>(dataList -> {

                for (Object o : dataList) {
                    //将导入的数据用mybatisPlus一个个添加进数据库
                    //loginMapper.insert(user);
                    dataListFromReturn.add(o);
                }
            })).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("excel导入失败");
        }
        return dataListFromReturn;
    }

    /**
     *
     * @param pathName 文件路径
     * @param pathTypeCode 路径类型
     * @return java.util.Map
     * @author XuLeHuang
     * @create 2023/2/19
     * @name:
     * @Description: 将json内容转化成Map
     **/
    public static Map readFromJsonFile(String pathName, Integer pathTypeCode) {
        String respString = "";
        try ( FileInputStream file = new FileInputStream(pathName);
              InputStreamReader reader = new InputStreamReader(file,"UTF-8");
              BufferedReader br = new BufferedReader(reader);)  {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
                respString += line;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //将文件内容转成Map返回
        HashMap objectObjectHashMap = (HashMap)JSONObject.parseObject(respString ,HashMap.class);
        return objectObjectHashMap;
    }

    /**
     *
     * @param filePath 文件路径
     * @param data 数据
     * @return void
     * @author XuLeHuang
     * @create 2023/3/6
     * @name: 大文件写出
     * @Description: 采用mmp（内存映射）方式
     **/
    public static void bigDataWrite(String filePath , Object data) {
        RandomAccessFile raf = null;
        FileChannel rafchannel = null;
        MappedByteBuffer map = null;
        try {
            raf = new RandomAccessFile(filePath, "rw");
            rafchannel = raf.getChannel();
            //mmap  堆外  和文件映射的   byte  not  objtect
            map = rafchannel.map(FileChannel.MapMode.READ_WRITE, 0, data.toString().getBytes().length);
            map.put(data.toString().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rafchannel != null) {
                    rafchannel.close();
                }
                if (raf != null) {
                    raf.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("随机文件对象和channel对象关闭失败");
            }
        }
    }

    /**
     *
     * @param content
     * @param filePath
     * @param pathTypeCode
     * @return boolean
     * @author XuLeHuang
     * @create 2023/3/21
     * @name: 文件覆盖写
     * @Description:
     **/
    public static boolean writeFileByOverride(String content , String filePath , Integer pathTypeCode) {
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        byte[] content2Bytes = content.getBytes();
        int length = content2Bytes.length;
        try {
            bufferedOutputStream.write(content2Bytes , 0 , length);
            bufferedOutputStream.flush();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void asyncRead() {
        Path path = Paths.get("data/test.xml");
        AsynchronousFileChannel fileChannel = null;
        try {
            fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;
        fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("result = " + result);

                attachment.flip();
                byte[] data = new byte[attachment.limit()];
                attachment.get(data);
                System.out.println(new String(data));
                attachment.clear();
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {

            }
        });

    }

    /**
     * 路径类型枚举
     */
    public static enum PathTypeCode {
        ABSOLUTE_PATH(000, "绝对路径"),
        CLASS_PATH(001, "类路径");

        private static final Set<Integer> enumSet = new HashSet<>(2);

        static {
            Arrays.asList(FileUtil.PathTypeCode.values()).forEach(e -> enumSet.add(e.getCode()));
        }

        public static boolean hasEnum(Integer code) {
            return enumSet.contains(code);
        }


        private Integer code;

        private String desc;

        PathTypeCode(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public Integer getCode() {
            return code;
        }

        public String getDesc() {
            return desc;
        }

        public static String getDesc(Integer code) {
            FileUtil.PathTypeCode[] values = values();
            if (code == null)
                return null;
            for (FileUtil.PathTypeCode value : values) {
                if (value.code.equals(code)) {
                    return value.desc;
                }
            }
            return null;
        }
    }


    /**
     *
     * @param name
     * @return long
     * @author XuLeHuang
     * @create 2023/3/23
     * @name: 获取文件大小
     * @Description:
     **/
    public static long getFileContentLength(String name) {
        File file = new File(name);
        return file.exists() && file.isFile() ? file.length() : 0;
    }

    public static void main(String[] args) throws IOException, URISyntaxException {
        //System.out.println(walkFileAndReturnFileNameList(null, null));
        //xmlToJavaObject(null, "user.xml");
//        JSONObject jsonObject = new JSONObject();
//        jsonObject.put("a" , "111");
//        bigDataWrite("D:\\bigDataWrite.txt" , jsonObject);
        //String readCommonFileAndToStr = readCommonFileAndToStr(PathTypeCode.ABSOLUTE_PATH.getCode(), "D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\module\\dynamic-env-refresh-server\\src\\main\\resources\\serviceCallList.txt");
        //System.out.println(readCommonFileAndToStr);
String str = "";
        System.out.println(str.trim() == null);
    }
}
